package com.noah2021.service.impl;

import com.noah2021.dao.OrderDOMapper;
import com.noah2021.dao.SequenceDOMapper;
import com.noah2021.dao.StockLogDOMapper;
import com.noah2021.error.BusinessException;
import com.noah2021.error.EmBusinessError;
import com.noah2021.mq.MqProducer;
import com.noah2021.pojo.OrderDO;
import com.noah2021.pojo.SequenceDO;
import com.noah2021.pojo.StockLogDO;
import com.noah2021.service.ItemService;
import com.noah2021.service.OrderService;
import com.noah2021.service.UserService;
import com.noah2021.service.model.ItemModel;
import com.noah2021.service.model.OrderModel;
import com.noah2021.service.model.UserModel;
import com.noah2021.util.Common;
import org.junit.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 〈〉
 *
 * @author Noah2021
 * @create 2021/2/9
 * @return
 */
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private SequenceDOMapper sequenceDOMapper;

    @Autowired
    private ItemService itemService;

    @Autowired
    private UserService userService;

    @Autowired
    private OrderDOMapper orderDOMapper;

    @Autowired
    private StockLogDOMapper stockLogDOMapper;

    OrderModel decMysql(Integer userId, Integer itemId, Integer promoId,
                        Integer amount, String stockLogId,  ItemModel itemModel )
            throws BusinessException {
        //        因为这里的 异常会让redis回滚 所以不用写inc 的逻辑
        //订单入库
        OrderModel orderModel = new OrderModel();
        orderModel.setUserId(userId);
        orderModel.setItemId(itemId);
        orderModel.setAmount(amount);
        if (promoId != null)
            orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
        else
            orderModel.setItemPrice(itemModel.getPrice());

        orderModel.setPromoId(promoId);
        //这里不再是商品价格itemModel.getPrice()而是之前订单价格orderModel.getItemPrice()
        BigDecimal orderPrice = orderModel.getItemPrice().multiply(new BigDecimal(amount));
        orderModel.setOrderPrice(orderPrice);
        orderModel.setId(generateOrderNo());
        //返回前端
        OrderDO orderDO = convertFromOrderModel(orderModel);
//        这里出错的 都会回滚 所以不用手动写失败的 rollback逻辑
        //插入到orderinfo表
        orderDOMapper.insertSelective(orderDO);

//        假设他 插入order的时候出错了
//        boolean debug=true;
////        这里啥事情没有 应该不会出错 decMysql 函数应该可能会出错
//        if(debug){
//            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
//        }

        //增加销量
        itemService.increaseSales(itemId, amount);

        //设置库存流水状态为成功
        StockLogDO stockLogDO = stockLogDOMapper.selectByPrimaryKey(stockLogId);
        if(stockLogDO == null)
//            这里是什么问题 需要加库存吗 就是 rollback
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
//        stockLogDO.setStatus(2);
        stockLogDO.setStatus(Common.StockLogStatus.COMMIT_MESSAGE);
        stockLogDOMapper.updateByPrimaryKeySelective(stockLogDO);
        // 异步更新库存 被删了 为什么呢，这里怎么写的 inc 应该是 dec吧 虽然删掉了
        // 在上面已经减少了库存，但是上面是 redis 这里是mysql 不一样啊
//        这里是成功了 不用加库存 万一失败了呢
//        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
//            @Override
//            public void afterCommit() {
//                //异步更新库存
//                boolean mqResult = itemService.asyncDecreaseStock(itemId, amount);
////                if (!mqResult) {
////                    itemService.increaseStock(itemId, amount);
////                    throw new BusinessException(EmBusinessError.MQ_SEND_FAIL);
////                }
//            }
//        });
//        商品只要有id 就行 商品是什么都没关系 然而两张表 比如 食物 商品 和穿的商品，
//        两个各自的id  咋办捏
//      还是要保证商品只是一张表里的 id 是不会重复的

        return orderModel;
    }
    @Override
    @Transactional
    public OrderModel createOrder(Integer userId, Integer itemId, Integer promoId, Integer amount, String stockLogId) throws BusinessException {
        //入参校验
        //ItemModel itemModel = itemService.getItemById(itemId);
        ItemModel itemModel = itemService.getItemByIdInCache(itemId);
        if (itemModel == null) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "商品信息不存在");
        }
//
//        UserModel userModel = userService.getUserByIdInCache(userId);
//        if (userModel == null) {
//            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "用户信息不存在");
//        }
//        if (amount <= 0 || amount > 99) {
//            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "数量信息不正确");
//        }
//        if (promoId != null) {
//            if (promoId.intValue() != itemModel.getPromoModel().getId())
//                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "活动信息不正确");
//            else if (itemModel.getPromoModel().getStatus() != 2)
//                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "数量信息未开始");
//        }


        //下单方式：1.落单减库存    2.支付减库存：会造成某人已下完单，但是当付款成功的时候却没货
//        之前貌似是这里寄了 但是他的redis 的key 是什么时候放入的
// redis 减少库存，但是mysql没有变
        boolean decreaseStockOk = itemService.decreaseStock(itemId, amount);
//        可以在这里抛异常 测试，redis 减了 ，但是抛出异常之后没有rollback，可以认为
//        如果 下面的mysql 之类的出问题 redis也不会rollback吗
//        boolean debug=true;
////        这里啥事情没有 应该不会出错 decMysql 函数应该可能会出错
//        if(debug){
//            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
//        }
//        throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
        if (!decreaseStockOk){
//            log.
            System.out.println("库存不够了");
            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
//            boolean incStockOk = itemService.increaseStock(itemId, amount);
//            return null;
        }

        try{
            OrderModel orderModel = decMysql(userId, itemId, promoId,
                    amount, stockLogId, itemModel);
            return orderModel;
        }catch (Exception e){
            e.printStackTrace();
            boolean incStockOk = itemService.increaseStock(itemId, amount);
//            throw  new
//            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
            throw new BusinessException(EmBusinessError.mysqlError);
//            return null;

        }
//        因为这里的 异常会让redis回滚 所以不用写inc 的逻辑
        //订单入库
//        OrderModel orderModel = new OrderModel();
//        orderModel.setUserId(userId);
//        orderModel.setItemId(itemId);
//        orderModel.setAmount(amount);
//        if (promoId != null)
//            orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
//        else
//            orderModel.setItemPrice(itemModel.getPrice());
//
//        orderModel.setPromoId(promoId);
//        //这里不再是商品价格itemModel.getPrice()而是之前订单价格orderModel.getItemPrice()
//        BigDecimal orderPrice = orderModel.getItemPrice().multiply(new BigDecimal(amount));
//        orderModel.setOrderPrice(orderPrice);
//        orderModel.setId(generateOrderNo());
//        //返回前端
//        OrderDO orderDO = convertFromOrderModel(orderModel);
////        这里出错的 都会回滚 所以不用手动写失败的 rollback逻辑
//        //插入到orderinfo表
//        orderDOMapper.insertSelective(orderDO);
//        //增加销量
//        itemService.increaseSales(itemId, amount);
//
//        //设置库存流水状态为成功
//        StockLogDO stockLogDO = stockLogDOMapper.selectByPrimaryKey(stockLogId);
//        if(stockLogDO == null)
////            这里是什么问题 需要加库存吗 就是 rollback
//            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
////        stockLogDO.setStatus(2);
//        stockLogDO.setStatus(Common.StockLogStatus.COMMIT_MESSAGE);
//        stockLogDOMapper.updateByPrimaryKeySelective(stockLogDO);



        // 异步更新库存 被删了 为什么呢，这里怎么写的 inc 应该是 dec吧 虽然删掉了
        // 在上面已经减少了库存，但是上面是 redis 这里是mysql 不一样啊
//        这里是成功了 不用加库存 万一失败了呢
//        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
//            @Override
//            public void afterCommit() {
//                //异步更新库存
//                boolean mqResult = itemService.asyncDecreaseStock(itemId, amount);
////                if (!mqResult) {
////                    itemService.increaseStock(itemId, amount);
////                    throw new BusinessException(EmBusinessError.MQ_SEND_FAIL);
////                }
//            }
//        });
//        商品只要有id 就行 商品是什么都没关系 然而两张表 比如 食物 商品 和穿的商品，
//        两个各自的id  咋办捏
//      还是要保证商品只是一张表里的 id 是不会重复的

//        return orderModel;
    }


    //生成商品订单流水号
    /*
    public enum Propagation {
    REQUIRED(0),   //表示开启一个事务并在事务之中，当已经在一个事务中就可以不用开启
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),  //无论有没有开启事务都必须重新开启一个新的事务
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);
    * */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public String generateOrderNo() {
        StringBuilder stringBuilder = new StringBuilder();
        LocalDateTime now = LocalDateTime.now();
//         ISO_8601日期格式标准. 当前的UTC时间是2016-01-07T01:58Z
//        https://zoucz.com/blog/2016/01/29/date-iso/
        String nowDate = now.format(DateTimeFormatter.ISO_DATE)
                .replace("-", "");
        stringBuilder.append(nowDate);

        int sequence = 0;
        SequenceDO sequenceDO = sequenceDOMapper.getSequenceByName("order_info");
        sequence = sequenceDO.getCurrentValue();
        sequenceDO.setCurrentValue(sequenceDO.getCurrentValue() + sequenceDO.getStep());
        //修改sequence_info的current_value
        sequenceDOMapper.updateByPrimaryKeySelective(sequenceDO);
        String sequenceStr = String.valueOf(sequence);
        for (int i = 0; i < 6 - sequenceStr.length(); i++)
            stringBuilder.append(0);
        stringBuilder.append(sequenceStr);
//最后2位为分库分表位,暂时写死
        stringBuilder.append("00");
        return stringBuilder.toString();
    }

    private OrderDO convertFromOrderModel(OrderModel orderModel) {
        if (orderModel == null) {
            return null;
        }
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderModel, orderDO);
        orderDO.setItemPrice(orderModel.getItemPrice().doubleValue());
        orderDO.setOrderPrice(orderModel.getOrderPrice().doubleValue());
        return orderDO;
    }

}
